package cn.sdormitory.common;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.ClassUtil;
import cn.sdormitory.common.utils.DateTimeUtils;
import cn.sdormitory.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AccessLevel;
import lombok.Getter;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

public class BaseForm<Form extends BaseForm<Form, Entity>, Entity> {

    @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8")
    @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
    private Date beginTime;
    @JsonFormat(pattern = DatePattern.NORM_DATETIME_PATTERN, timezone = "GMT+8")
    @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
    private Date endTime;

    @Getter(AccessLevel.NONE)
    private final Class<Entity> entityClass;

    protected BaseForm(){
        //noinspection unchecked
        entityClass = (Class<Entity>) ClassUtil.getTypeArgument(getClass(),1);
    }

    public Entity generateBean(Class<Entity> beanClass){
        return BeanUtil.copyProperties(this, beanClass);
    }

    public LambdaQueryWrapper<Entity> lambdaQueryOrderByDescId(){
        return queryOrderByDescId().lambda();
    }

    public LambdaQueryWrapper<Entity> lambdaQuery(){
        return query().lambda();
    }

    public QueryWrapper<Entity> queryOrderByDescId(){
        return query().orderByDesc("id");
    }

    public QueryWrapper<Entity> queryOrderBySort(){
        return query().orderByAsc("sort");
    }

    public QueryWrapper<Entity> query(){
        QueryWrapper<Entity> wrapper = Wrappers.query(generateBean(entityClass));
        bindDateRange(wrapper);
        return wrapper;
    }

    private void bindDateRange(QueryWrapper<Entity> wrapper) {
        if(beginTime == null && endTime == null){
            return;
        }
        if(beginTime != null && endTime != null){
            wrapper.between(getDateRangeColumn(), beginTime, endTime);
        }else if(beginTime != null){
            wrapper.ge(getDateRangeColumn(), beginTime);
        }else {
            wrapper.le(getDateRangeColumn(), endTime);
        }
    }

    protected String getDateRangeColumn() {
        return "create_time";
    }

    public String getColumn(SFunction<Entity,?> sFunction){
        SerializedLambda lambda = LambdaUtils.resolve(sFunction);
        String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
        Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(entityClass);
        if(columnMap == null){
            throw new IllegalArgumentException("根据"+entityClass.getSimpleName()+"获取columnMap失败");
        }
        fieldName = LambdaUtils.formatKey(fieldName);
        ColumnCache columnCache = columnMap.get(fieldName);
        if(columnCache == null){
            throw new IllegalArgumentException("根据"+entityClass.getSimpleName()+"."+fieldName+"获取列名失败");
        }
        return columnCache.getColumn();
    }

    protected String handleStr(String str){
        return StringUtils.isBlank(str) ? null : str;
    }

    public Form setBeginTime(Date beginTime) {
        this.beginTime = beginTime;
        //noinspection unchecked
        return (Form) this;
    }

    public Form setEndTime(Date endTime) {
        this.endTime = endTime;
        //noinspection unchecked
        return (Form) this;
    }

    public Form validateThrow(){
        validateThrowNative();
        //noinspection unchecked
        return (Form) this;
    }

    protected void validateThrowNative() {
    }

    @SuppressWarnings("UnusedReturnValue")
    protected Form betweenDay(QueryWrapper<Entity> wrapper, SFunction<Entity,?> columnFunction, Date startDate){
        return betweenDay(wrapper,columnFunction,startDate,1);
    }

    @SuppressWarnings("SameParameterValue")
    protected Form betweenDay(QueryWrapper<Entity> wrapper, SFunction<Entity,?> columnFunction, Date startDate, int dayOffset){
        if(startDate == null){
            //noinspection unchecked
            return (Form) this;
        }
        String column = getColumn(columnFunction);
        Date startDay = DateTimeUtils.toDay(startDate);
        Date endDay = DateTimeUtils.addDay(startDay, dayOffset);
        wrapper.between(column,startDay,endDay);
        //noinspection unchecked
        return (Form) this;
    }

}